package someTestExcemple.thread;

import com.sun.corba.se.spi.orbutil.threadpool.ThreadPool;

import java.util.concurrent.*;

public class ThreadPoolTest {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        test1();
    }
    public static void test1() throws ExecutionException, InterruptedException {
        //1.通过ThreadPoolExecutor创建线程池
        ExecutorService pool =
                new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,
                        new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());
        //((ThreadPoolExecutor) pool).allowCoreThreadTimeOut(true);
        ThreadPoolExecutor poolT =
                new ThreadPoolExecutor(3, 5, 8, TimeUnit.SECONDS,
                        new ArrayBlockingQueue<>(4), Executors.defaultThreadFactory(),
                        new ThreadPoolExecutor.AbortPolicy());
        //设置自定义拒绝策略
        poolT.setRejectedExecutionHandler(new CustomRejectedExecutionHandler());


        //上面几个参数分别是：
        //1.核心线程数---核心线程不会因为空闲而被销毁，除非显式调用 allowCoreThreadTimeOut(true) 方法允许核心线程超时释放。
        //2.线程池最大线程数 --当核心线程数达到最大线程数，且任务队列已满，才会创建新线程。
        //3.线程空闲时间 --线程空闲时间是一般指线程池中超过核心线程数的线程(非核心线程)
        // 在空闲状态下的最大存活时间。
        // 如果一个线程在指定的空闲时间内没有新的任务分配给它，该线程将会被终止以释放资源。
        //4.时间单位
        //5.任务队列 --线程池满后，新任务会进入队列，如果队列已满，则创建新线程，直到线程数达到最大线程数，
        //6.线程工厂 --定义创建线程的方式
        //7.拒绝策略

        /**
         * Java 中 ThreadPoolExecutor 提供了以下几种内置的拒绝策略，用于处理线程池已满（达到最大线程数且任务队列已满）时的情况：
         * AbortPolicy：直接抛出 RejectedExecutionException 异常，终止程序运行。这是默认的拒绝策略。
         * CallerRunsPolicy：由调用线程（提交任务的线程）来执行该任务，这会降低提交任务的线程的响应速度。
         * DiscardPolicy：静默丢弃无法处理的任务，不抛出任何异常。
         * DiscardOldestPolicy：丢弃队列中最旧的一个任务（即等待时间最长的任务），然后尝试重新提交当前任务。
         * 这些策略可以通过 ThreadPoolExecutor 的构造函数或 setRejectedExecutionHandler 方法进行设置。
         *
         */
        /**
         * 代码解释
         * 新任务会进入到任务队列中的条件是：当线程池中当前运行的线程数已经达到核心线程数（corePoolSize），并且没有空闲线程可以立即执行新任务时，新任务会被放入任务队列中等待执行。具体流程如下：
         * 线程池初始化：线程池创建后，默认情况下可能不会立即启动任何线程，除非有任务提交。
         * 提交任务：当调用 execute 方法提交任务时，线程池会检查当前运行的线程数是否小于核心线程数。
         * 如果小于核心线程数，则创建新线程来执行任务。
         * 如果等于核心线程数，则尝试将任务放入任务队列中。
         * 任务队列入口条件：当核心线程都在忙且无法立即处理新任务时，新任务会被放入任务队列中等待。
         * 在代码中，ThreadPoolExecutor 的构造函数定义了核心线程数为 3，最大线程数为 5，任务队列容量为 4。因此：
         * 当前运行线程数小于等于 3 时，优先创建核心线程来执行任务。
         * 当核心线程数已满（即 3 个线程都在工作）时，新任务会被放入任务队列中。
         * 如果任务队列也满了（即已有 4 个任务在队列中等待），才会考虑创建超过核心线程数的额外线程（最多到最大线程数 5）。
         */

        Runnable runnable = new MyRunnable2(); // 创建任务
        pool.execute(runnable); //线程池自动创建新线程，自动处理任务
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        pool.execute(runnable);
        Callable<String> myCallable = new MyCallable1(8);
        Future<String> future =  pool.submit(myCallable);
        System.out.println(future.get());
        pool.shutdown(); //等待线程池的任务执行完毕，再关闭线程池  --实际开发中，线程池一般不会去关闭
        //pool.shutdownNow(); //立刻关闭线程池，并清空任务队列

    }

    public static void test2() {
        //2.通过Executors工具类创建线程池
        //创建固定线程数量的线程池  --本质还是通过ThreadPoolExecutor创建的线程池
        ExecutorService pool =Executors.newFixedThreadPool(3);
        //但是上面这种创建线程池，允许请求队列长度为Integer.MAX_VALUE，在高并发场景下会出现
        //大量请求堆积，从而导致OOM(内存溢出)
        //经验之谈:线程池的核心线程数应该配置成多少
        //如果是计算密集型任务，那么线程池的核心线程数应该配置成CPU核数 +1
        //如果是IO密集型任务，那么线程池的核心线程数应该配置成CPU核数 *2
        //设置为当前系统处理器的个数*2
        ExecutorService pool1 =Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()*2);
        ExecutorService pool2 =Executors.newSingleThreadExecutor();
        /**
         * Executors 类提供了多种静态方法来创建不同类型的线程池。这些方法简化了 ThreadPoolExecutor 的配置，适用于不同的应用场景。以下是几种常用的 Executors 创建线程池的方法：
         * newFixedThreadPool(int nThreads)：
         * 核心线程数 和 最大线程数 都设置为 nThreads。
         * 任务队列 使用 LinkedBlockingQueue，容量为 Integer.MAX_VALUE。
         * 适用场景：适用于执行大量短期异步任务，且任务数量相对稳定的场景。
         * newCachedThreadPool()：
         * 核心线程数 设置为 0。
         * 最大线程数 设置为 Integer.MAX_VALUE。
         * 线程空闲时间 设置为 60 秒。
         * 任务队列 使用 SynchronousQueue，不存储元素。
         * 适用场景：适用于执行大量短期异步任务，且任务数量波动较大的场景。
         * newSingleThreadExecutor()：
         * 核心线程数 和 最大线程数 都设置为 1。
         * 任务队列 使用 LinkedBlockingQueue，容量为 Integer.MAX_VALUE。
         * 适用场景：适用于需要保证任务按顺序执行的场景。
         * newScheduledThreadPool(int corePoolSize)：
         * 核心线程数 设置为 corePoolSize。
         * 最大线程数 设置为 Integer.MAX_VALUE。
         * 线程空闲时间 设置为 0 秒。
         * 任务队列 使用 DelayedWorkQueue。
         * 适用场景：适用于需要执行定时任务或周期性任务的场景。
         * newWorkStealingPool(int parallelism)：
         * 核心线程数 和 最大线程数 根据系统可用处理器数确定。
         * 任务队列 使用 ForkJoinPool 的工作窃取算法。
         * 适用场景：适用于执行大量可并行的任务，利用多核处理器的优势。
         */
        ScheduledExecutorService pool3 = Executors.newScheduledThreadPool(3);
        //创建一个延迟3秒执行的定时任务
        pool3.schedule(new MyRunnable2(), 3, TimeUnit.SECONDS);
        //创建一个延迟1秒，每隔2秒执行的定时任务
        pool3.scheduleAtFixedRate(new MyRunnable2(), 1, 2, TimeUnit.SECONDS);
    }
}
